# Cache

import index from '!!raw-loader!@site/docs/examples/requests-cache.ex';
import { LiveDemo } from '@site/components/LiveDemo';

Using this feature, you can manage the cache status of API calls in your store. First, you need to install the package
by using the CLI command `elf-cli install` and selecting the requests package, or via npm:

```bash
npm i @ngneat/elf-requests
```

To use this feature, provide the `withRequestsCache` props factory function in the `createStore` call:

```ts title="todos.repository.ts"
import { createStore } from '@ngneat/elf';
import { withEntities } from '@ngneat/elf-entities';
import { withRequestsCache } from '@ngneat/elf-requests';

interface Todo {
  id: number;
  label: string;
}

const todosStore = createStore(
  { name: 'todos' },
  withEntities<Todo>(),
  // You can pass the keys type
  // highlight-next-line
  withRequestsCache<'todo' | `todo-${string}`>(),
);
```

Now we can use the `createRequestsCacheOperator` function that takes a store and returns a custom operator.

```ts
import {
  withRequestsCache,
  createRequestsCacheOperator,
} from '@ngneat/elf-requests';

const todosStore = createStore(
  { name: 'todos' },
  withEntities<Todo>(),
  withRequestsCache<'todo' | `todo-${string}`>(),
);

export const skipWhileTodosCached = createRequestsCacheOperator(todosStore);
```

We can use the resulting operator and pass a unique key to identify the request. This enables skipping the API call if the passed key is located in the store cache.

```ts title="todos.service.ts"
import { setTodos, skipWhileTodosCached } from './todos.repository';

export function fetchTodos() {
  return http.get(todosUrl).pipe(
    tap(setTodos(todos)),
    // highlight-next-line
    skipWhileTodosCached('todos'),
  );
}
```

Use it in tandem with `updateRequestsCache`:

```ts title="todos.repository.ts"
import { updateRequestCache } from '@ngneat/elf-requests';
import { setEntities } from '@ngneat/elf-entities';

export function setTodos(todos: Todo[]) {
  store.update(
    // highlight-next-line
    updateRequestCache('todos'),
    setEntities(todos),
  );
}
```

Passing a value as the third parameter ensures the `store` will only skip the API call if the value matches the
one passed. Values can be 'none', 'partial' or 'full':

```ts title="todos.repository.ts"
import { updateRequestCache } from '@ngneat/elf-requests';

export function setTodos(todos: Todo[]) {
  store.update(
    // highlight-next-line
    updateRequestCache('todos', { value: 'partial' }),
    setEntities(todos),
  );
}
```

```ts title="todos.service.ts"
import { setTodos, skipWhileTodosCached } from './todos.repository';

export function fetchTodos() {
  return http.get(todosUrl).pipe(
    tap((todos) => setTodos(todos)),
    // highlight-next-line
    skipWhileTodosCached('todos', { value: 'partial' }),
  );
}
```

In addition to value, you can pass in the same object a `returnSource` which will be returned by the operator if the
request is cached. The default return value is `EMPTY` observable.

```ts title="todos.service.ts"
import { skipWhileTodosCached, setTodos } from './todos.repository';

export function fetchTodos() {
  return http.get(todosUrl).pipe(
    tap((todos) => setTodos(todos)),
    // highlight-next-line
    skipWhileTodosCached('todos', { returnSource: of([]) }),
  );
}
```

<LiveDemo src={index} packages={['entities', 'requests']} />
<br />

You can monitor and change the request cache status for your APIs using the following queries and mutations:

## Queries

### `selectRequestCache`

Select the cache status of the provided request key:

```ts
import { selectRequestCache } from '@ngneat/elf-requests';

todosCacheStatus$ = store.pipe(selectRequestCache('todos'));
```

### `getRequestCache`

Get the cache status of the provided request key:

```ts
import { getRequestCache } from '@ngneat/elf-requests';

todosCacheStatus = store.query(getRequestCache('todos'));
```

### `selectIsRequestCached`

Select whether the cache status of the provided request key isn't `none`:

```ts
import { selectIsRequestCached } from '@ngneat/elf-requests';

const isCached$ = store.pipe(selectIsRequestCached('todos'));
const isPartialCached$ = store.pipe(
  selectIsRequestCached('todos', { value: 'partial' }),
);
```

Get whether the cache status of the provided request key isn't `none`:

```ts
import { isRequestCached } from '@ngneat/elf-requests';

const isCached = store.query(isRequestCached('todos'));
const isPartialCached = store.query(
  isRequestCached('todos', { value: 'partial' }),
);
```

## Mutations

### `updateRequestCache`

```ts
import { updateRequestCache } from '@ngneat/elf-requests';

store.update(updateRequestCache('todos'));
store.update(updateRequestCache('todos', { value: 'partial' }));
store.update(updateRequestCache('todos', { value: 'none' }));
store.update(updateRequestCache('todos', { ttl: 1000 }));
```

If you pass `ttl` (time to live) when updating a cache record, that represents the time (in milliseconds) that `key` will
have the value that was set (afterward, it reverts to 'none').

### `updateRequestsCache`

```ts
import { updateRequestsCache } from '@ngneat/elf-requests';

store.update(
  updateRequestsCache({
    keyOne: {
      value: 'partial',
    },
  }),
);

store.update(updateRequestsCache(['keyOne', 'keyTwo'], { value: 'partial' }));

store.update(
  updateRequestsCache(['keyOne', 'keyTwo'], { value: 'partial', ttl: 1000 }),
);
```

If you pass `ttl` (time to live) when updating a cache record, that represents the time (in milliseconds) that key will
have the value that was set (afterward, it reverts to 'none'). This parameter can be used to set individual `ttl` values for each key when updating multiple keys at once. If a `ttl` is not passed for a key, the value for that key does not expire.

### `clearRequestsCache`

```ts
import { clearRequestsCache } from '@ngneat/elf-requests';

store.update(clearRequestsCache());
```

### `deleteRequestsCache`

```ts
import { deleteRequestsCache } from '@ngneat/elf-requests';

store.update(deleteRequestsCache('keyOne'));

store.update(deleteRequestsCache(['keyOne', 'keyTwo']));
```
